package abel.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

public class HexString {
//    private transient byte[] bs;
    private String hex;

    public HexString() {
    }

    public HexString(String hex) {
        setHex(hex);
    }

    public HexString(String hex, String splitor) {
//        this.bs = hexStringToBytes(hex, splitor);
//        this.hex = bytesToHexStringWithBlank(bs);
        setHex(hex);
    }

    /*
    public HexString(byte[] bs) {
        setBs(bs);
    }
    */

    /**
     * Constructs from ByteBuf.
     * 
     * @param buf
     * @param size
     */
    public HexString(ByteBuf buf, int size) {
        if (buf.readableBytes() < size) {
            throw new IllegalArgumentException("not enough bytes in buffer");
        }

        byte[] bs = new byte[size];
        buf.readBytes(bs);

        hex = bytesToHexString(bs);
    }

    public HexString(ByteBuf buf) {
        this(buf, buf.readableBytes());
    }

    /**
     * @return the bs
     */
    public byte[] getBs() {
        return hexStringToByte(hex);
    }

    /**
     * @param bs
     *            the bs to set
     */
    /*
    public void _setBs(byte[] bs) {
        this.bs = bs;
        hex = HexString.bytesToHexStringWithBlank(bs);
    }
    */

    /**
     * @return the hex
     */
    public String getHex() {
        return hex;
    }

    /**
     * @param hex
     *            the hex to set
     */
    public void setHex(String hex) {
        hex = hex.trim();
        this.hex = hex.contains(" ") ? bytesToHexString(hexStringToByte(hex)) : hex;
//        bs = HexString.hexStringToByte(hex);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{hex:'%s'}", hex);
    }

    /**
     * Gets length of bytes.
     * 
     * @return
     */
    public int length() {
        return hexStringToByte(hex).length;
    }

    public static byte[] hexStringToByte(String hex) {
        hex = hex.trim();
        return hex.contains(" ") ? _hexWithBlankToByte(hex) : _hexStringToByte(hex);
    }

    /**
     * 把16进制字符串转换成字节数组
     * 
     * @param hex
     * @return
     */
    public static byte[] _hexStringToByte(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] =
                    (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    public static byte[] _hexWithBlankToByte(String hex) {
        return _hexStringToBytes(hex, " ");
    }

    /**
     * Parse the string with splitor and return byte array.
     * 
     * @param hex
     * @param splitor
     * @return
     */
    public static byte[] _hexStringToBytes(String hex, String splitor) {
        String[] strList = hex.split(splitor);
        ByteBuf buf = Unpooled.buffer();
        for (String str : strList) {
            if (str.trim().length() > 0) {
                buf.writeByte(Short.parseShort(str.trim(), 16));
            }
        }

        byte[] ret = new byte[buf.readableBytes()];
        buf.readBytes(ret);
        return ret;
    }

    static byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**
     * 把字节数组转换成16进制字符串
     * 
     * @param bArray
     * @param begin
     * @param end
     *            - not included.
     * @return
     */
    public static final String bytesToHexString(byte[] bArray) {
        return bytesToHexString(bArray, 0, bArray.length);
    }

    /**
     * 把字节数组转换成16进制字符串
     * 
     * @param bArray
     * @param begin
     * @param end
     *            - not included.
     * @return
     */
    public static final String bytesToHexString(byte[] bArray,
            int begin,
            int end) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = begin; i < end; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 把字节数组转换成16进制字符串
     * 
     * @param bArray
     * @param begin
     * @param end
     *            - not included.
     * @return
     */
    public static final String bytesToHexStringWithBlank(byte[] bArray) {
        return bytesToHexStringWithBlank(bArray, 0, bArray.length);
    }

    /**
     * 把字节数组转换成16进制字符串. It doesn't disturb reader index of the buf.
     * 
     * @param bArray
     * @param begin
     * @param end
     *            - not included.
     * @return
     */
    public static final String bytesToHexStringWithBlank(ByteBuf buf) {
        byte[] bArray = new byte[buf.readableBytes()];
        buf.duplicate().readBytes(bArray);
        return bytesToHexStringWithBlank(bArray, 0, bArray.length);
    }

    /**
     * 把字节数组转换成16进制字符串
     * 
     * @param bArray
     * @param begin
     * @param end
     *            - not included.
     * @return
     */
    public static final String bytesToHexStringWithBlank(byte[] bArray,
            int begin,
            int end) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = begin; i < end; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase()).append(' ');
        }
        return sb.toString().trim();
    }

    /**
     * 把字节数组转换为对象
     * 
     * @param bytes
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static final Object bytesToObject(byte[] bytes) throws IOException,
            ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        ObjectInputStream oi = new ObjectInputStream(in);
        Object o = oi.readObject();
        oi.close();
        return o;
    }

    /**
     * 把可序列化对象转换成字节数组
     * 
     * @param s
     * @return
     * @throws IOException
     */
    public static final byte[] objectToBytes(Serializable s) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream ot = new ObjectOutputStream(out);
        ot.writeObject(s);
        ot.flush();
        ot.close();
        return out.toByteArray();
    }

    public static final String objectToHexString(Serializable s)
            throws IOException {
        return bytesToHexString(objectToBytes(s));
    }

    public static final Object hexStringToObject(String hex)
            throws IOException, ClassNotFoundException {
        return bytesToObject(hexStringToByte(hex));
    }

    /**
     * MD5加密字符串，返回加密后的16进制字符串
     * 
     * @param origin
     * @return
     */
    public static String MD5EncodeToHex(String origin) {
        return bytesToHexString(MD5Encode(origin));
    }

    /**
     * MD5加密字符串，返回加密后的字节数组
     * 
     * @param origin
     * @return
     */
    public static byte[] MD5Encode(String origin) {
        return MD5Encode(origin.getBytes());
    }

    /**
     * MD5加密字节数组，返回加密后的字节数组
     * 
     * @param bytes
     * @return
     */
    public static byte[] MD5Encode(byte[] bytes) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
            return md.digest(bytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return new byte[0];
        }

    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof HexString) || obj == null) {
            return false;
        }

        HexString hstr = (HexString) obj;
        if (hex == null && hstr.hex == null) {
            return true;
        } else if (hex == null || hstr.hex == null) {
            return false;
        }

        /*
        if (bs.length != hstr.bs.length) {
            return false;
        }
        
        for (int i = 0; i < bs.length; i++) {
            if (bs[i] != hstr.bs[i]) {
                return false;
            }
        }
        */

        return hex.equals(hstr.hex);
    }

}
